﻿using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using FscNET.Apm;
using FscNET.NamedPipes;
using FscNET.Tracer.Properties;
using FscNET.Tracing;
using System.Collections.Generic;

namespace FscNET.Tracer
{
    public delegate void Action();

    public partial class Main : Form
    {
        private const int BUFFER_LEN = 4096;
        private static readonly ASCIIEncoding _encoder = new ASCIIEncoding();

        [DllImport("user32")] private static extern bool HideCaret(IntPtr hWnd);

        private const string ColCategory = "Category";
        private const string ColProcess = "Process";
        private const string ColThread = "Thread";
        private const string ColModule = "Module";
        private const string ColProject = "Project";
        private const string ColFunction = "Function";
        private const string ColFile = "File";
        private const string ColLine = "Line";
        private const string ColStack = "Stack";
        private const string ColUser = "User";
        private const string ColMessage = "Message";

        private readonly BindingListEx<TraceEntry> _traces = new BindingListEx<TraceEntry>();

        //private PipeServer _pipeServer;
        private bool _tracing;

        private static char[] _nullChar = new char[] { '\0' };

        private List<NamedPipeServerStream> _pipeList = new List<NamedPipeServerStream>(16);
        private List<AsyncEnumerator> _aeList = new List<AsyncEnumerator>(16);

        public Main()
        {
            InitializeComponent();
            InitializeMenuItemGroups();
            HideCaret(textBox.Handle);
            //_pipeServer = new PipeServer(Settings.Default.ServerPipeName);
            Shown += delegate { Start(); };
            tsbtnStartStop.Click += delegate {
                if (_tracing)
                    Stop();
                else 
                    Start();
            };
            FormClosing += Main_FormClosing;
            toolStripMenuItem1.Click += TEST;

            //_pipeServer.Start();
            tsbtnConnection.Image = Resources.connected16;
            tsbtnConnection.Text = "Odpojiť";

            tscomboPipeServerName.Text = Settings.Default.ServerPipeName;

            //display datagrid view
            //textBox.Visible = false;
            InitializeDataGridView();
            InitializeMenu();

            AsyncEnumerator ae = new AsyncEnumerator();
            ae.BeginExecute(PipeServerEnumerator(ae), ae.EndExecute, null);
        }

        private void InitializeDataGridView()
        {
            dgwTraces.DataSource = _traces;
            DataGridViewTextBoxColumn category = new DataGridViewTextBoxColumn();
            category.Name = ColCategory;
            category.DataPropertyName = "Category";

            DataGridViewTextBoxColumn process = new DataGridViewTextBoxColumn();
            process.Name = ColProcess;
            process.DataPropertyName = "Process";

            DataGridViewTextBoxColumn thread = new DataGridViewTextBoxColumn();
            thread.Name = ColThread;
            thread.DataPropertyName = "Thread";

            DataGridViewTextBoxColumn module = new DataGridViewTextBoxColumn();
            module.Name = ColModule;
            module.DataPropertyName = "Module";

            DataGridViewTextBoxColumn project = new DataGridViewTextBoxColumn();
            project.Name = ColProject;
            project.DataPropertyName = "Project";

            DataGridViewTextBoxColumn function = new DataGridViewTextBoxColumn();
            function.Name = ColFunction;
            function.DataPropertyName = "Function";

            DataGridViewTextBoxColumn file = new DataGridViewTextBoxColumn();
            file.Name = ColFile;
            file.DataPropertyName = "File";

            DataGridViewTextBoxColumn line = new DataGridViewTextBoxColumn();
            line.Name = ColLine;
            line.DataPropertyName = "Line";

            DataGridViewTextBoxColumn stack = new DataGridViewTextBoxColumn();
            stack.Name = ColStack;
            stack.DataPropertyName = "Stack";

            DataGridViewTextBoxColumn user = new DataGridViewTextBoxColumn();
            user.Name = ColUser;
            user.DataPropertyName = "User";

            DataGridViewTextBoxColumn message = new DataGridViewTextBoxColumn();
            message.Name = ColMessage;
            message.DataPropertyName = "Message";
            //message.DefaultCellStyle.WrapMode = DataGridViewTriState.True;

            dgwTraces.AutoGenerateColumns = false;
            dgwTraces.Columns.Clear();

            dgwTraces.Columns.Add(category);
            dgwTraces.Columns.Add(process);
            dgwTraces.Columns.Add(thread);
            dgwTraces.Columns.Add(module);
            dgwTraces.Columns.Add(project);
            dgwTraces.Columns.Add(function);
            dgwTraces.Columns.Add(file);
            dgwTraces.Columns.Add(line);
            dgwTraces.Columns.Add(stack);
            dgwTraces.Columns.Add(user);
            dgwTraces.Columns.Add(message);

            //attempt to set up soft wrapping of text in cells as suggested on 
            //http://social.msdn.microsoft.com/Forums/en/winformsdatacontrols/thread/fbb6dcc9-b9ae-4865-9974-97251e4c7987
            //not working...
            dgwTraces.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCellsExceptHeader;
            dgwTraces.DefaultCellStyle.WrapMode = DataGridViewTriState.True;
            dgwTraces.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;
            //dgwTraces.DefaultCellStyle.WrapMode = DataGridViewTriState.False;

            //dgwTraces.ColumnHeadersVisible = false;
            SetColumnsVisibility();
            dgwTraces.Font = new System.Drawing.Font("Courier New", 8f);
        }

        private void SetColumnsVisibility()
        {
            dgwTraces.Columns[ColProcess].Visible = OutputManager.Process;
            dgwTraces.Columns[ColThread].Visible = OutputManager.Thread;
            dgwTraces.Columns[ColModule].Visible = OutputManager.Module;
            dgwTraces.Columns[ColProject].Visible = OutputManager.Project;
            dgwTraces.Columns[ColFunction].Visible = OutputManager.Function;
            dgwTraces.Columns[ColFile].Visible = OutputManager.File;
            dgwTraces.Columns[ColLine].Visible = OutputManager.Line;
            dgwTraces.Columns[ColStack].Visible = OutputManager.Stack;
            dgwTraces.Columns[ColUser].Visible = OutputManager.User;
        }

        private void InitializeMenu()
        {
            //miOutput.DropDown.AutoClose = false;
            miOutput.DropDown.Closing += miOutput_DropDownClosing;
            miOutput.DropDownItemClicked += miOutput_DropDownItemCLicked;
            miOutputProcess.Checked = OutputManager.Process;
            miOutputThread.Checked = OutputManager.Thread;
            miOutputModule.Checked = OutputManager.Module;
            miOutputProject.Checked = OutputManager.Project;
            miOutputFunction.Checked = OutputManager.Function;
            miOutputFile.Checked = OutputManager.File;
            miOutputLine.Checked = OutputManager.Line;
            miOutputStack.Checked = OutputManager.Stack;
            miOutputUser.Checked = OutputManager.User;
        }

// ReSharper disable FunctionRecursiveOnAllPaths
        private IEnumerator<int> PipeServerEnumerator(AsyncEnumerator ae)
// ReSharper restore FunctionRecursiveOnAllPaths
        {
            using (NamedPipeServerStream pipe = new NamedPipeServerStream(
                Settings.Default.ServerPipeName,
                PipeDirection.In, 
                16, 
                PipeTransmissionMode.Message, 
                PipeOptions.Asynchronous | PipeOptions.WriteThrough ))
            {
                _pipeList.Add(pipe);
                _aeList.Add(ae);
                //wait fofr client
                pipe.BeginWaitForConnection(ae.End(), null);
                yield return 1;

                //client connected, wait for new client
                  AsyncEnumerator aeNew = new AsyncEnumerator();
                  aeNew.BeginExecute(PipeServerEnumerator(aeNew), aeNew.EndExecute, null);

                //accept client connection

                pipe.EndWaitForConnection(ae.DequeueAsyncResult());

                if(ae.IsCanceled()) yield break;

                //read messages from client
                //AsyncEnumerator aeRead = new AsyncEnumerator();
                byte[] buffer = new byte[BUFFER_LEN];
                while (true)
                {
                    int bytesRead;
                    pipe.BeginRead(buffer, 0, BUFFER_LEN, ae.End(), null);
                    yield return 1;

                    try
                    {
                        bytesRead = pipe.EndRead(ae.DequeueAsyncResult());
                        if (bytesRead == 0) yield break;
                    }
                    catch
                    {
                        yield break;
                    }
                    ProcessMessage(Encoding.UTF8.GetString(buffer, 0, bytesRead));
                    //ProcessMessage(_encoder.GetString(buffer, 0, bytesRead));
                }
            }
        }


        private void ProcessMessage(string message)
        {

            //write to datagridview
            if (InvokeRequired)
            {
                Invoke((Action)delegate {
                    _traces.Add(message);
                    textBox.AppendText(message.TrimEnd(_nullChar) + Environment.NewLine);
                });
            }
            else
            {
                _traces.Add(message);
               textBox.AppendText(message.TrimEnd(_nullChar) + Environment.NewLine);
            }

            //write to textbox
            //if (textBox.IsDisposed) return;

            //Debug.Write(message); Debug.WriteLine(""); Debug.WriteLine(message); Debug.WriteLine("");
            //string[] lines = message.Split(new char[] { '\0' }, StringSplitOptions.RemoveEmptyEntries);
            //if (textBox.InvokeRequired)
            //{
            //    BeginInvoke((Action)delegate {
            //        foreach (string line in lines)
            //            textBox.AppendText(line + (_startWithNewLine ? Environment.NewLine : ""));
            //    });
            //}
            //else
            //    foreach (string line in lines)
            //        textBox.AppendText(line + (_startWithNewLine ? Environment.NewLine : ""));

            //_startWithNewLine = message.EndsWith("\0");
        }

        private void tsbtnClearTextBox_Click(object sender, EventArgs e)
        {
            textBox.Clear();
        }

        private void Start()
        {
            tsbtnStartStop.Image = Resources.pause_16;
            tsbtnStartStop.Text = @"Pozastaviť";
            _tracing = true;
            //if(_pipeServer != null)
            //    _pipeServer.MessageReceived += WriteToDataGridView;
            //    //_pipeServer.MessageReceived += WriteToTextBox;
        }

        private void Stop()
        {
            tsbtnStartStop.Image = Resources.start_16;
            tsbtnStartStop.Text = @"Spustiť";
            _tracing = false;
            //if (_pipeServer != null)
            //    _pipeServer.MessageReceived -= WriteToDataGridView;
            //    //_pipeServer.MessageReceived += WriteToTextBox;
        }

        private void tsbtnConnection_Click(object sender, EventArgs e)
        {
            //if (_pipeServer.Running)
            //{
            //    //_pipeServer.Disconnect();
            //    tsbtnConnection.Image = Resources.disconnected16;
            //    tsbtnConnection.Text = "Pripojiť";
            //}
            //else
            //{
            //    string name = string.IsNullOrEmpty(tscomboPipeServerName.Text.Trim()) ? Settings.Default.ServerPipeName :
            //        tscomboPipeServerName.Text.Trim();

            //    _pipeServer.PipeName = name;
            //   // _pipeServer.Connect();
            //    tsbtnConnection.Image = Resources.connected16;
            //    tsbtnConnection.Text = "Odpojiť";
            //}
        }

        private void tsbtnAlwaysOnTop_CheckStateChanged(object sender, EventArgs e)
        {
            TopMost = tsbtnAlwaysOnTop.Checked;
        }

        struct WaitCursor : IDisposable
        {
            private Control _c;
            public WaitCursor(Control control)
            {
                _c = control;
                if (_c != null) _c.Cursor = Cursors.WaitCursor;
            }

            public void Dispose()
            {
                if (_c != null) _c.Cursor = Cursors.Default;
            }
        }
    }

}
